// Copyright 2019 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------
// Modification history:
// feature: Unified Transport Abstraction Layer Unified Transport Abstraction Layer
// ------------------------------------------------------------------

#ifndef SRC_CPP_RTPS_TRANSPORT_TCPCHANNELRESOURCEBASIC_H_
#define SRC_CPP_RTPS_TRANSPORT_TCPCHANNELRESOURCEBASIC_H_

#include <mutex>
#include <asio.hpp>
#include <TCPChannelResource.h>

using AsyncSendHandler = std::function<void(const asio::error_code&, size_t)>;
namespace vbs {
namespace transport {
class TCPChannelResourceBasic : public TCPChannelResource {
    asio::io_service& service_;

    std::mutex send_mutex_;
    std::shared_ptr<asio::ip::tcp::socket> socket_;

 public:
    // Constructor called when trying to connect to a remote server
    TCPChannelResourceBasic(TCPTransportInterface* parent, asio::io_service& service, const Locator& locator,
                            uint32_t maxMsgSize);
    TCPChannelResourceBasic(TCPTransportInterface* parent, asio::io_service& service, const Locator& locator,
                            const Locator& remote_locator, uint32_t maxMsgSize);

    // Constructor called when local server accepted connection
    TCPChannelResourceBasic(TCPTransportInterface* parent, asio::io_service& service,
                            std::shared_ptr<asio::ip::tcp::socket> socket, const Locator& locator,
                            const Locator& remote_locator, uint32_t maxMsgSize);

    TCPChannelResourceBasic(TCPTransportInterface* parent, asio::io_service& service,
                            std::shared_ptr<asio::ip::tcp::socket> socket, const Locator& locator, uint32_t maxMsgSize);

    virtual ~TCPChannelResourceBasic();

    virtual bool connect(const std::shared_ptr<TCPChannelResource>& myself) override;

    void disconnect() override;

    uint32_t read(ertps::rtps::octet* buffer, std::size_t size, asio::error_code& ec) override;

    size_t send(const ertps::rtps::octet* header, size_t header_size, const ertps::rtps::octet* data, size_t size,
                asio::error_code& ec) override;

    void async_send(const octet* header, size_t header_size, const octet* data, size_t size, AsyncSendHandler handler,
                    bool is_default_header = false) override;

    asio::ip::tcp::endpoint remote_endpoint() const override;
    asio::ip::tcp::endpoint local_endpoint() const override;

    void set_options(const TCPTransportDescriptor* options) override;

    void cancel() override;
    void close() override;
    void shutdown(asio::socket_base::shutdown_type what) override;

    inline std::shared_ptr<asio::ip::tcp::socket> socket() { return socket_; }

    void endpoint_to_locator(const asio::ip::tcp::endpoint& endpoint, Locator& locator) const {
        locator.kind = LOCATOR_KIND_TCPv4;
        (void)IPLocator::setPhysicalPort(locator, endpoint.port());
        auto ipBytes = endpoint.address().to_v4().to_bytes();
        (void)IPLocator::setIPv4(locator, ipBytes.data());
    }

 private:
    TCPChannelResourceBasic(const TCPChannelResourceBasic&) = delete;
    TCPChannelResourceBasic& operator=(const TCPChannelResourceBasic&) = delete;
};

}  // namespace transport
}  // namespace vbs

#endif  // SRC_CPP_RTPS_TRANSPORT_TCPCHANNELRESOURCEBASIC_H_
